#pragma once

#include "Item.h"

class ItemStorage
{
public:
	ItemStorage(Player* pOwner);
	~ItemStorage();

	Player* GetOwner() const { return m_pOwner; }
	uint32 GetBagCapacity() const { return m_bagCapacity; }
	uint32 GetBankCapacity() const { return m_bankCapacity; }

	void SetBagCapacity(uint32 capacity);
	void SetBankCapacity(uint32 capacity);

	Item* GetItem(ItemSlotType type, uint32 slot) const;
	void ForeachItem(ItemSlotType type,
		const std::function<void(uint32 i, Item*)>& func) const;

	GErrorCode UseItem(uint32 slot,
		uint32 num = 1, const std::string_view& udata = emptyStringView);
	GErrorCode DestroyItem(uint32 slot, uint32 num = 1);

	bool IsItemEnough(
		uint32 itemTypeID, uint32 itemCount, uint32 flags = 0) const;
	bool IsItemsEnough(const uint32 itemTypeIDs[],
		size_t itemSize, uint32 itemCount, uint32 flags = 0) const;
	uint32 GetItemAmount(uint32 itemTypeID,
		uint32 flags = 0, uint32 hints = UINT32_MAX) const;
	uint32 GetItemsAmount(const uint32 itemTypeIDs[], size_t itemSize,
		uint32 flags = 0, uint32 hints = UINT32_MAX) const;
	GErrorCode RemoveItemAmount(uint32 itemTypeID, uint32 itemCount,
		ITEM_FLOW_TYPE flowType, params<uint32> flowParams, uint32 flags = 0);
	GErrorCode RemoveItemsAmount(
		const uint32 itemTypeIDs[], size_t itemSize, uint32 itemCount,
		ITEM_FLOW_TYPE flowType, params<uint32> flowParams, uint32 flags = 0);
	GErrorCode RemoveItemAmountBySlot(ItemSlotType type, uint32 itemSlot,
		uint32 itemCount, ITEM_FLOW_TYPE flowType, params<uint32> flowParams);

	void CreateAddItemMail(
		const ItemPrototype* pItemProto, const inst_item_prop& itemProp,
		ITEM_FLOW_TYPE flowType, params<uint32> flowParams, bool isSendPopMsg = false,
		Item** pNewItemPtr = NULL);
	void CreateAddItemsMail(const ItemPrototype* const* pItemProtos,
		const inst_item_prop itemProps[], size_t itemSize,
		ITEM_FLOW_TYPE flowType, params<uint32> flowParams, bool isSendPopMsg = false,
		Item** pNewItemPtr = NULL);

	GErrorCode CreateAddItem(
		const ItemPrototype* pItemProto, const inst_item_prop& itemProp,
		ITEM_FLOW_TYPE flowType, params<uint32> flowParams, bool isSendPopMsg = false,
		uint32* pItemSurplusNum = NULL, Item** pNewItemPtr = NULL);
	uint32 GetFillItemLackSlotCount(
		const ItemPrototype* pItemProto, const inst_item_prop& itemProp,
		bool canMergeable = true) const;
	GErrorCode CreateAddItems(const ItemPrototype* const* pItemProtos,
		const inst_item_prop itemProps[], size_t itemSize,
		ITEM_FLOW_TYPE flowType, params<uint32> flowParams, bool isSendPopMsg = false,
		uint32** pItemSurplusNums = NULL, Item** pNewItemPtr = NULL);
	uint32 GetFillItemsLackSlotCount(
		const ItemPrototype* const* pItemProtos, const inst_item_prop itemProps[],
		size_t itemSize, bool canMergeable = true) const;

	GErrorCode ArrangeItems(ItemSlotType type);
	GErrorCode SwapItem(ItemSlotType type, uint32 slot,
		ItemSlotType tgtType, uint32 tgtSlot = ItemSlotInvalid);
	GErrorCode SplitItem(ItemSlotType type, uint32 slot, uint32 num);

	GErrorCode EquipItem(uint32 slot, uint32 tgtSlot = ItemSlotInvalid);
	GErrorCode UnequipItem(uint32 slot, uint32 tgtSlot = ItemSlotInvalid);

	void BuildCreatePacketForPlayer(INetPacket& pck, Player* pPlayer);

	std::string SaveEffectItems() const;
	std::string SaveAllOtherItems() const;
	void LoadEffectItems(const std::string& data);
	void LoadAllOtherItems(const std::string& data);

	std::string SaveStorageStatus() const;
	void LoadStorageStatus(const std::string& data);

private:
	GErrorCode CanItemUse4Loot(
		Item* pItem, uint32 num, const std::string_view& udata) const;
	int UseItem4Loot(uint32 actionUniqueKey,
		Item* pItem, uint32 num, const std::string_view& udata) const;
	GErrorCode CanItemUse4Spell(
		Item* pItem, uint32 num, const std::string_view& udata) const;
	int UseItem4Spell(uint32 actionUniqueKey,
		Item* pItem, uint32 num, const std::string_view& udata) const;
	GErrorCode CanItemUse4Script(
		Item* pItem, uint32 num, const std::string_view& udata) const;
	int UseItem4Script(uint32 actionUniqueKey,
		Item* pItem, uint32 num, const std::string_view& udata) const;

	std::pair<Item* const*, uint32> GetItemCluster(
		ItemSlotType type, uint32 slot = ItemSlotInvalid) const;
	std::pair<Item**, uint32> GetItemClusterMutable(
		ItemSlotType type, uint32 slot = ItemSlotInvalid);

	void RemoveCountSlotItem(ItemSlotType type, uint32 slot,
		uint32 num, ITEM_FLOW_TYPE flowType, params<uint32> flowParams);
	void RemoveSlotItem(ItemSlotType type,
		uint32 slot, ITEM_FLOW_TYPE flowType, params<uint32> flowParams);

	void AddNewItem2FreeSlot(ItemSlotType type, uint32 slot,
		Item* pItem, ITEM_FLOW_TYPE flowType, params<uint32> flowParams,
		uint32 flags = 0);
	Item* NewItem(const ItemPrototype* pItemProto,
		const inst_item_prop& itemProp, uint32 itemCount = 0,
		bool isNew = true) const;

	void SwapItem(Item** pItems1, ItemSlotType type1, uint32 slot1,
		Item** pItems2, ItemSlotType type2, uint32 slot2,
		ITEM_FLOW_TYPE flowType, params<uint32> flowParams);

	void SaveItemCluster(
		Item* const pItems[], uint32 slotCapacity, TextPacker& packer) const;
	void LoadItemCluster(ItemSlotType slotType,
		Item* pItems[], uint32 slotCapacity, TextUnpacker& unpacker) const;

	bool CanFilterItem(const ItemPrototype* pItemProto) const;
	bool FilterItem(const ItemPrototype* pItemProto,
		const inst_item_prop& itemProp);

	static bool CanMergeItem(Item* pItem, Item* pOtherItem);
	static bool CanMergeItem(
		Item* pItem, const inst_item_prop& itemProp, uint32 extItemNum = 0);
	static uint32 GetFreeSlotCount(Item* const pItems[], uint32 slotCapacity);
	static uint32 GetFreeSlot(Item* const pItems[], uint32 slotCapacity);

	static void PackItemCluster(
		INetPacket& pck, Item* const pItems[], uint32 slotCapacity);

	Player* const m_pOwner;
	Item* m_equipItems[ItemEquipSlotCount];
	Item* m_bagItems[ItemBagSlotCount];
	Item* m_bankItems[ItemBankSlotCount];
	uint32 m_bagCapacity;
	uint32 m_bankCapacity;
};
